// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.jasmin.unit.ula;

import com.dustedpixels.jasmin.unit.Unit;

/**
 * Provided with current ROW and COL, it fetches data from video memory and
 * decodes it into SCREEN and ATTR values on output plus CONTENTION pin. SCREEN
 * and ATTR can be directly connected to ColorDecoder unit.
 * 
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class MemoryDecoder implements Unit {
  // (input) clock source (7Mhz)
  public boolean CLOCK;

  // (input) row and column counter
  public int ROW;
  public int COL;

  // (output) contention
  public boolean CONTENTION = false;

  // (output, tristate) address of screen and attr bytes
  public int ADDR = 0;
  public int ADDR_Z = 0;

  // (input) screen and addr data.
  public int DATA = 0;

  // (output, tristate) memory request
  public boolean MREQ = true;
  public boolean MREQ_Z = false;

  // (output) properly synced screen and attribute data
  public int SCREEN = 0;
  public int ATTR = 0;

  public int screen;
  public int attr;

  private boolean lastClock = false;
  private int address = 0;

  public void updateEndPoints() {
    if (lastClock != CLOCK) {
      if (CLOCK) {
        if (ROW == 0 && COL == 0) address = 0;
        if (ROW >= 48 && ROW < 48 + 192 && COL >= 46 && COL < 48 + 256) {
          switch (COL & 0x0F) {
            case 0x00:
              // Leave hi-Z state of ADDR and MREQ pins.
              // Request screen byte
              ADDR = (address & 0x181F) | ((address << 3) & 0x0700) | ((address >>> 3) & 0x00E0);
              //System.out.println(Integer.toHexString(ADDR));
              ADDR_Z = ~0;
              MREQ_Z = true;
              break;
            case 0x01:
              // Read and store screen byte.
              screen = DATA;
              break;
            case 0x02:
              // Request attribute byte
              ADDR = 0x1800 | (address & 0x001F) | ((address >>> 3) & 0x03E0);
              //System.out.println(Integer.toHexString(ADDR));
              break;
            case 0x03:
              // Output screen and attribute bytes read so far.
              // Increment video memory address.
              SCREEN = screen;
              ATTR = DATA;
              address++;
              break;
            case 0x04:
              // Request screen byte
              ADDR = (address & 0x181F) | ((address << 3) & 0x0700) | ((address >>> 3) & 0x00E0);
              //System.out.println(Integer.toHexString(ADDR));
              break;
            case 0x05:
              // Read and store screen byte
              screen = DATA;
              break;
            case 0x06:
              // Request attribute byte
              ADDR = 0x1800 | (address & 0x001F) | ((address >>> 3) & 0x03E0);
              //System.out.println(Integer.toHexString(ADDR));
              break;
            case 0x07:
              // Read and store attribute byte
              attr = DATA;
              break;
            case 0x08:
              // Go back to hi-Z state for ADDR and MREQ pins.
              ADDR_Z = 0;
              MREQ_Z = false;
              break;
            case 0x0A:
              // Leave contention
              CONTENTION = false;
              break;
            case 0x0B:
              // Output screen and attribute bytes.
              // Increment address
              SCREEN = screen;
              ATTR = attr;
              address++;
              break;
            case 0x0E:
              // Enter contention
              CONTENTION = true;
              break;
          }
        }
      }
      lastClock = CLOCK;
    }
  }
}
